#include <stdio.h>
#include "board.h"
#include "hpm_pwm_drv.h"

#define BEEP_PWM BOARD_BEEP_PWM
#define BEEP_OUT BOARD_BEEP_PWM_OUT
#define BEEP_CLK BOARD_BEEP_PWM_CLOCK_NAME

void gen_pwm_to_beep(uint32_t pwm_freq, uint32_t duration_ms)
{
    uint8_t cmp_index = 0;
    bool increase_duty_cycle = true;
    pwm_cmp_config_t cmp_config = {0};
    pwm_config_t pwm_config = {0};
    uint32_t clk_freq = 0;
    uint32_t pwm_reload = 0;

    // 计算reload值
    // 因为， pwm_freq = clk_freq / (reload + 1)
    // 所以， reload = clk_freq / pwm_freq - 1
    clk_freq = clock_get_frequency(BEEP_CLK);
    pwm_reload = clk_freq / pwm_freq - 1;
    printf("clk_freq = %d, pwm_reload = %d\n", clk_freq, pwm_reload);

    // 准备pwm_config
    pwm_get_default_pwm_config(BEEP_PWM, &pwm_config); // 填充 pwm_config_t 默认值
    pwm_config.enable_output = true;
    pwm_config.dead_zone_in_half_cycle = 0;
    pwm_config.invert_output = false;

    // 准备cmp_config
    pwm_get_default_cmp_config(BEEP_PWM, &cmp_config); // 填充 pwm_cmp_config_t 默认值
    cmp_config.mode = pwm_cmp_mode_output_compare; // 设置PWM工作模式为输出
    cmp_config.cmp = pwm_reload / 2;  // 设置初始CMP值，这样直接设置为 1/2 则后续不需要更新即可生成 50% 占空比
    // cmp_config.cmp = pwm_reload + 1; // CMP > RLD, 由于计数器值 CNT 始终达不到 CMPx，比较器输出 OCx 会保持逻辑 0
    cmp_config.update_trigger = pwm_shadow_register_update_on_modify; // 设置CMP影子寄存器值生效时刻为 更新后的下一个周期
    // pwm_shadow_register_update_on_modify 这种方式下一个指令周期就会重装CMP，可能会导致PWM波形不完整，手册不推荐这种方式

    //pwm_stop_counter(BEEP_PWM); // 停止计数（没有也可以）
    pwm_set_reload(BEEP_PWM, 0, pwm_reload); // 设置RLD寄存器
    pwm_set_start_count(BEEP_PWM, 0, 0); // 设置STA寄存器

    // 使用给定参数对PWM通道进行设置
    if (status_success != pwm_setup_waveform(BEEP_PWM, BEEP_OUT, &pwm_config, cmp_index, &cmp_config, 1)) {
        printf("failed to setup waveform\n");
        while(1);
    }

    pwm_start_counter(BEEP_PWM); // 开始计数（PWM输出开始）
    pwm_issue_shadow_register_lock_event(BEEP_PWM); // 锁定影子寄存器

    // 和 cmp = pwm_reload + 1 一起使用，也可以得到 50% 占空比的 PWM波形
    // 在这里更新CMP影子寄存器，下一个周期CMP寄存器会得到更新，这种方式便于动态更新PWM波形
    // pwm_update_raw_cmp_edge_aligned(BEEP_PWM, cmp_index, pwm_reload / 2); // 50 % HIGH

    board_delay_ms(duration_ms); // 按参数延时一段时间，这段时间一直会由PWM波形输出
    pwm_disable_output(BEEP_PWM, BEEP_OUT); // 停止输出PWM信号
}

// 记谱方式参考：https://ost.51cto.com/posts/1511
// 《东方红》简谱： https://zhidao.baidu.com/question/467535829.html
static const uint16_t g_tuneFreqs[] = { // 音符对应的分频系数
    0, // 1=F4 (1=F4 2=G4 3=A4 4=Bb4 5=C5 6=D5 7=E5)
    349, // 1 349.23
    392, // 2 392.0
    440, // 3 440.0
    466, // 4 466.16
    523, // 5 523.25
    587, // 6 587.33
    659, // 7 659.26
    698, // 1^ 698.46 // 高一个八度的
    262, // 5_ 261.63 // 低一个八度的
    294, // 6_ 293.66
    330, // 7_ 329.63
};

enum {
    H1 = 8,
    L5 = 9,
    L6 = 10,
    L7 = 11,
};

// 曲谱音符
static const uint8_t g_scoreNotes[] = {
//  1,  .,  2,  .;  1,  .,  2,  .;  1,  .,  2,  .;  1,  .,  2,  .;  1,  .,  2,  .;
    5,      5,  6,  2,              1,      1, L6,  2,           //
    5,      5,      6, H1,  6,  5,  1,      1, L6,  2,           //
    5,      2,      1,     L7, L6, L5,      5,      2,      3,  2,  1,      1, L6,//
    2,  3,  2,  1,  2,  1, L7, L6, L5,             L5,           //
};

// 曲谱时值，根据简谱记谱方法转写
static const uint8_t g_scoreDurations[] = {
//  1,  .,  2,  .;  1,  .,  2,  .;  1,  .,  2,  .;  1,  .,  2,  .;  1,  .,  2,  .;
    2,      1,  1,  4,              2,      1,  1,  4,           //
    2,      2,      1,  1,  1,  1,  2,      1,  1,  4,           //
    2,      2,      2,      1,  1,  2,      2,      2,      1,  1,  2,      1,  1,//
    1,  1,  1,  1,  1,  1,  1,  1,  4,              4,           //
};

int main(void)
{
    board_init();
    board_init_led_pins();
    board_init_beep_pwm_pins();
    printf("pwm_beep start!\n");

    printf("Generate PWM waveform to BEEP...\n");
    // gen_pwm_to_beep(440, 1000);

    for (size_t i = 0; i < sizeof(g_scoreNotes)/sizeof(g_scoreNotes[0]); i++) {
        uint32_t tune = g_scoreNotes[i]; // 音符
        uint16_t freq = g_tuneFreqs[tune] * 2; // *2 升一个八度，蜂鸣器听起来更响；不升也是可以的
        uint32_t intval = g_scoreDurations[i] * (500); // 音符时间
        printf("[%d]: %d %d %d\n", i, tune, freq, intval);
        board_led_toggle();
        gen_pwm_to_beep(freq, intval);
    }

    printf("pwm_beep done!\n");
    while(1);

    return 0;
}
